package soul.rpc;

import server.FooService;
import server.SrpcRequest;
import server.SrpcResponse;
import soul.utils.IDGenerator;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Proxy;
import java.net.Socket;

/**
 * Created by sunzhengjie on 2017/1/5.
 */
public class RpcClient {

    public static void main(String[] arg0) throws Exception {
        FooService service = refer(FooService.class, "127.0.0.1", 12000);
        service.hello("hello world");
    }

    /**
     * 引用服务
     *
     * @param <T>            接口泛型
     * @param interfaceClass 接口类型
     * @param host           服务器主机名
     * @param port           服务器端口
     * @return 远程服务
     * @throws Exception
     */
    @SuppressWarnings("unchecked")
    public static <T> T refer(final Class<T> interfaceClass, final String host, final int port)
            throws Exception {
        if (interfaceClass == null || !interfaceClass.isInterface()) {
            throw new IllegalArgumentException("必须指定服务接口");
        }

        if (host == null || host.length() == 0) {
            throw new IllegalArgumentException("必须指定服务器的地址和端口号");
        }

        /**
         代理interface每个方法，调用方法时发送interface、方法、参数到server端获取response**/
        return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(),
                new Class<?>[]{interfaceClass}, (proxy, method, arguments) -> {
                    Socket socket = new Socket(host, port);
                    try {
                        ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
                        try {
                            SrpcRequest request = new SrpcRequest();
                            request.setRequestId(IDGenerator.generateId().toString());
                            request.setInterfaceName(interfaceClass.getName());
                            request.setMethodName(method.getName());
                            request.setParameterTypes(method.getParameterTypes());
                            request.setParameters(arguments);
                            output.writeObject(request);
                            ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
                            try {
                                SrpcResponse response = (SrpcResponse)input.readObject();
                                if (response.getError() != null
                                        && response.getError() instanceof Throwable) {
                                    throw response.getError();
                                }
                                return response.getResult();
                            } finally {
                                input.close();
                            }
                        } finally {
                            output.close();
                        }
                    } finally {
                        socket.close();
                    }
                });
    }
}
